/*
 * Copyright (c) 2023 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include <mc/log.h>

#include "hi183x_card.h"

namespace dev {

void hi183x_card::init_network_ports() {
    auto objects = get_children();
    if (objects.empty()) {
        elog("Error: No network ports found");
        return;
    }
    for (auto& object : objects) {
        auto object_name = object->get_name();
        if (object_name.substr(0, object_name.find_first_of("_")) == "NicPort") {
            ilog("hi183x_card init network port ${name}", ("name", object_name));
            m_network_ports.push_back(static_cast<hi183x_port*>(object.get()));
        }
    }
}
// 实现pcie_nic_card的虚函数
bool hi183x_card::start() {
    init_network_ports();
    for (auto& port : m_network_ports) {
        port->start();
    }
    start_protocol();
    update_max_sfp_temperature();
    return true;
}

// 实现pcie_nic_card的虚函数
bool hi183x_card::stop() {
    return true;
}

void hi183x_card::replace_dynamic_property(mc::mutable_dict& object, const mc::dict& connector) {
    m_pcie_device.replace_dynamic_property(object, connector);
    m_board.replace_dynamic_property(object, connector);
}
// 实现pcie_nic_card的虚函数
bool hi183x_card::init(mc::mutable_dict& csr_object, const mc::dict& connector) {
    try {
        from_variant(csr_object, *this);
    } catch (const std::exception& e) {
        elog("hi183x_card init failed, exception: ${exception}", ("exception", e.what()));
        return false;
    }
    return true;
}

bool hi183x_card::start_ncsi_protocol() {
    if ((m_pcie_device.DevBus == 0) && (m_pcie_device.DevDevice == 0) &&
        (m_pcie_device.DevFunction == 0)) {
        dlog("PCIe device bdf not initialized");
        return false;
    }
    uint16_t phy_addr = mctp::init_phy_addr(m_pcie_device.DevBus, m_pcie_device.DevDevice,
                                            m_pcie_device.DevFunction);

    mctp* mctp_object = new mctp(this, phy_addr, MCTP_MESSAGE_TYPE::MCTP_MESSAGE_TYPE_NCSI, "");

    auto ncsi_update_task = [this, mctp_object]() {
        m_ncsi_over_mctp_huawei = std::make_shared<ncsi_over_mctp_huawei>(*mctp_object);
        for (auto& port : m_network_ports) {
            port->start_ncsi_protocol(m_ncsi_over_mctp_huawei, m_interval);
        }
        m_pcie_device.start_ncsi_update_task_huawei(m_ncsi_over_mctp_huawei, m_interval);
        m_pcie_device_bandwidth.start_ncsi_update_task_huawei(m_ncsi_over_mctp_huawei, m_interval);
        m_network_adapter.start_ncsi_update_task_huawei(m_ncsi_over_mctp_huawei, m_interval);
        m_network_adapter_fault_status.start_ncsi_update_task_huawei(m_ncsi_over_mctp_huawei,
                                                                     m_interval);
        m_network_adapter_cooling.start_ncsi_update_task_huawei(m_ncsi_over_mctp_huawei,
                                                                m_interval);
    };
    mctp_object->create_transport_and_endpoint(std::string(get_object_name()), ncsi_update_task);
    return true;
}

bool hi183x_card::start_protocol() {
    if (!start_ncsi_protocol()) {
        m_pcie_device.pcie_device_bdf_changed.connect(
            [this](uint8_t bus, uint8_t device, uint8_t function) {
                ilog("PCIe device bdf(${bus}:${device}:${function}) changed, start ncsi protocol",
                     ("bus", bus)("device", device)("function", function));
                start_ncsi_protocol();
            });
        return false;
    }
    return true;
}

bool hi183x_card::update_max_sfp_temperature() {
    uint16_t max_temperature = 0;
    uint8_t  status          = 2;
    for (auto& port : m_network_ports) {
        auto& om_cooling_ref = port->m_optical_module->m_optical_module_cooling;
        if ((om_cooling_ref.TemperatureStatus != 0xFFFF) &&
            (om_cooling_ref.TemperatureCelsius > max_temperature)) {
            max_temperature = om_cooling_ref.TemperatureCelsius;
            status          = 0;
        }
    }
    m_network_adapter_cooling.update_max_sfp_temperature(max_temperature, status);
    return true;
}

// 异步更新所有端口的BDF信息
void hi183x_card::update_ports_bdf_async(imu_pcie_device_req_t& pcie_device_info) {
    for (auto& port : m_network_ports) {
        bool is_success = port->update_port_bdf(pcie_device_info);
        if (!is_success) {
            elog("Error: Failed to update port(${port_id}) BDF",
                 ("port_id", port->m_network_port.PortId));
        }
    }
}

} // namespace dev

MC_REFLECT(dev::hi183x_card,
           ((m_system_id, "SystemId"))((m_pcie_device, "bmc.dev.PCIeDevice"))(
               (m_pcie_card, "bmc.dev.PCIeCard"))((m_network_adapter, "bmc.dev.NetworkAdapter"))(
               (m_network_adapter_fault_status, "bmc.dev.NetworkAdapter.FaultStatus"))(
               (m_network_adapter_cooling, "bmc.dev.NetworkAdapter.Cooling"))(
               (m_board, "bmc.dev.Board"))((m_pcie_device_bandwidth, "PCIeDevice.Bandwidth"))(
               (m_network_adapter_log_collection, "bmc.dev.NetworkAdapter.LogCollection")))